package com.zxjbyte.yiyi.framework.common.core.excel;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import com.google.common.collect.Sets;
import com.zxjbyte.yiyi.framework.common.exception.ServiceException;
import com.zxjbyte.yiyi.framework.common.util.CollXUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 根据指定列，对数据行进行合并单元格
 *
 * 同时支持 手动指定行进行分段合并
 *
 * @Author zhangxingjia
 * @Date 2023/7/6 16:38
 * @Version: 1.0
 */
public class MergeStrategy extends AbstractMergeStrategy {

    // 合并的列编号，从0开始，指定的index或自己按字段顺序数
    private final Set<Integer> mergeCellIndex = new HashSet<>();

    private List<Integer> beginSecMergeRowIdxList;
    private List<Integer> endSecMergeRowIdxList;

    // 数据集大小，用于区别结束行位置
    private Integer maxRow = 0;

    // 禁止无参声明
    private MergeStrategy() {
    }

    public MergeStrategy(Integer maxRow, int... mergeCellIndex) {
        this(maxRow, null, mergeCellIndex);
    }

    public MergeStrategy(Integer maxRow, List<List<Integer>> secMergeRowIdx, int... mergeCellIndex) {
        Arrays.stream(mergeCellIndex).forEach(this.mergeCellIndex::add);
        if(CollXUtil.isNotEmpty(secMergeRowIdx)){
            /**
             * 不指定全局合并，指定跟开始索引至结束索引之间进行分段合并，从0开始为行绝对编号
             *  例如：
             *  list.add(1, 1);  第一行数据单独一行
             *  list.add(2, 3);  第二行至第三行之间进行合并处理
             *  list.add(4, 5);  第四行至第五行之间进行合并处理
             *
             *  注意：如果指定了分段合并的区间，则只进行区间内合并处理，不在区间的行则不处理合并
             */
            secMergeRowIdx = secMergeRowIdx.stream().filter(f -> f.size() == 2).map(internalList -> internalList.stream().sorted().collect(Collectors.toList()))
                           .sorted(Comparator.comparingInt(o -> o.get(0))).collect(Collectors.toList());

            Set<Integer> uniqueSets = Sets.newHashSetWithExpectedSize(secMergeRowIdx.size() * 2);
            if(secMergeRowIdx.stream().anyMatch(list -> list.get(0).equals(list.get(1)) ? !uniqueSets.add(list.get(0))
                    : !uniqueSets.add(list.get(0)) || !uniqueSets.add(list.get(1)))){
                throw new ServiceException("The interval index of the specified segmented merge row is not repeatable");
            }
            beginSecMergeRowIdxList = secMergeRowIdx.stream().map(list -> list.get(0)).collect(Collectors.toList());
            endSecMergeRowIdxList = secMergeRowIdx.stream().map(list -> list.get(1)).collect(Collectors.toList());
        }
        this.maxRow = maxRow;
    }

    // 记录上一次合并的信息
    private final Map<Integer, MergeRange> lastRowMergeMap = new HashMap<>();

    // 记录分段合并行的开始数据索引
    private Integer secRowBeginIdx;


    // 每行每列都会进入，绝对不要在这写循环
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
        // 此单元格的列索引 从0开始
        int currentColIndex = cell.getColumnIndex();
        // 判断该行是否需要合并
        if (mergeCellIndex.contains(currentColIndex)) {
            String currentCellValue = cell.getStringCellValue();
            int currentRowIndex = cell.getRowIndex();
            // 指定进行分段行处理
            boolean openSecMergeRow = CollXUtil.isNotEmpty(beginSecMergeRowIdxList);
            boolean isSameRow  = false;
            if(openSecMergeRow){
                if(beginSecMergeRowIdxList.contains(currentRowIndex)){
                    int beginIndex = beginSecMergeRowIdxList.indexOf(currentRowIndex);
                    // 合并行区间的开始索引和结束索引相同则不合并
                    if(Objects.equals(endSecMergeRowIdxList.get(beginIndex), currentRowIndex)){
                        isSameRow = true;
                    }else {
                        secRowBeginIdx = beginIndex;
                        // 每段的开始行重新记录起始位置
                        lastRowMergeMap.remove(currentColIndex);
                    }
                }
            }
            if(isSameRow){
                return;
            }
            if (!lastRowMergeMap.containsKey(currentColIndex)) {
                // 记录首行起始位置
                lastRowMergeMap.put(currentColIndex, new MergeRange(currentRowIndex, currentRowIndex, currentColIndex, currentColIndex, currentCellValue));
                return;
            }
            //有上行这列的值了，拿来对比.
            MergeRange mergeRange = lastRowMergeMap.get(currentColIndex);
            if(secRowBeginIdx != null && Objects.equals(endSecMergeRowIdxList.get(secRowBeginIdx), currentRowIndex)){
                int endRow = Objects.equals(mergeRange.lastValue, currentCellValue)? currentRowIndex : mergeRange.endRow;
                sheet.addMergedRegionUnsafe(new CellRangeAddress(mergeRange.startRow, endRow, mergeRange.startCol, mergeRange.endCol));
                // 更新当前列起始位置
                lastRowMergeMap.put(currentColIndex, new MergeRange(currentRowIndex, currentRowIndex, currentColIndex, currentColIndex, currentCellValue));
            }
            if(!openSecMergeRow || secRowBeginIdx != null && currentRowIndex < endSecMergeRowIdxList.get(secRowBeginIdx)){
                if (!(mergeRange.lastValue != null && mergeRange.lastValue.equals(currentCellValue))) {
                    // 结束的位置触发下合并.
                    // 同行同列不能合并，会抛异常
                    if (mergeRange.startRow != mergeRange.endRow || mergeRange.startCol != mergeRange.endCol) {
                        sheet.addMergedRegionUnsafe(new CellRangeAddress(mergeRange.startRow, mergeRange.endRow, mergeRange.startCol, mergeRange.endCol));
                    }
                    // 更新当前列起始位置
                    lastRowMergeMap.put(currentColIndex, new MergeRange(currentRowIndex, currentRowIndex, currentColIndex, currentColIndex, currentCellValue));
                }
                // 合并行 + 1
                mergeRange.endRow += 1;
                // 结束的位置触发下最后一次没完成的合并
                if (relativeRowIndex.equals(maxRow - 1)) {
                    MergeRange lastMergeRange = lastRowMergeMap.get(currentColIndex);
                    // 同行同列不能合并，会抛异常
                    if (lastMergeRange.startRow != lastMergeRange.endRow || lastMergeRange.startCol != lastMergeRange.endCol) {
                        sheet.addMergedRegionUnsafe(new CellRangeAddress(lastMergeRange.startRow, lastMergeRange.endRow, lastMergeRange.startCol, lastMergeRange.endCol));
                    }
                }
            }
        }
    }

    private static class MergeRange {
        public int startRow;
        public int endRow;
        public int startCol;
        public int endCol;
        public String lastValue;

        public MergeRange(int startRow, int endRow, int startCol, int endCol, String lastValue) {
            this.startRow = startRow;
            this.endRow = endRow;
            this.startCol = startCol;
            this.endCol = endCol;
            this.lastValue = lastValue;
        }
    }
}
